import java.text.SimpleDateFormat;
import java.util.*;

public class TourOffering
{
    // date format
    private static SimpleDateFormat dateFormat;
    static
    {
        // month/day/year(12/10/2011)
        dateFormat = new SimpleDateFormat("MM/dd/yyyy");
        
        // time zone GMT for simple times since we aren't using date
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    }
    // ////////////////////////Instance Variables\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    Date startDate;
    Tour tour;
    boolean hold;
    Map<String, Reservation> reservations;
    List<Reservation> cancellations;
    
    // /////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    
    /**
     * initialize a tour offering
     */
    public TourOffering(Date start)
    {
        startDate = start;
        hold = false;
        reservations = new HashMap<String, Reservation>();
        cancellations = new LinkedList<Reservation>();
    }
    
    /**
     * adds a reservation to the offering
     * 
     * @param r
     *            reservation to be added
     */
    public void addReservation(Reservation r)
    {
        // if it is added as a single meaning it can take a partner,
        // see if we can find a partner for it
        if (r.getRoomType() == RoomType.SOLO)
        {
            for (Reservation res : reservations.values())
            {
                if (res.getRoomType() == RoomType.SOLO && res.getPartner() == null)
                {
                    res.setPartner(r.getTraveler());
                    r.setPartner(res.getTraveler());
                }
            }
        }
        
        // add it
        reservations.put(r.getTravelerName(), r);
    }
    
    /**
     * Simulate random cancellations by holding what we read in for later
     * 
     * @param r
     *            reservation to add to cancel
     */
    public void addCancellation(Reservation r)
    {
        cancellations.add(r);
    }
    
    /**
     * Apply cancellations
     * 
     * @return canceled reservations
     */
    public List<Reservation> applyCancellations()
    {
        List<Reservation> canceled = new LinkedList<Reservation>();
        
        for (Reservation r : cancellations)
        {
            canceled.addAll(cancelReservation(r));
        }
        
        cancellations.removeAll(cancellations);
        
        return canceled;
    }
    
    /**
     * set the tour object
     * 
     * @param t
     *            tour to be set
     */
    public void setTour(Tour t)
    {
        tour = t;
    }
    
    /**
     * puts the offering on hold
     */
    public void hold()
    {
        hold = true;
    }
    
    /**
     * takes the offering off hold if it isn't already off
     */
    public void offHold()
    {
        hold = false;
    }
    
    /**
     * 
     * @return the tour being offered
     */
    public Tour getTour()
    {
        return tour;
    }
    
    /**
     * @return if the tour is on hold
     */
    public boolean isOnHold()
    {
        return hold;
    }
    
    /**
     * 
     * @return start date of offering
     */
    public Date startDate()
    {
        return startDate;
    }
    
    /**
     * @return number of reservations in the offering
     */
    public int getNumReservations()
    {
        return reservations.size();
    }
    
    /**
     * Get a reservation by name
     * 
     * @param name
     *            name we are searching for
     * @return reservation by name
     */
    public Reservation getReservation(String name)
    {
        return reservations.get(name);
    }
    
    /**
     * Cancel a reservation
     * 
     * If it was reserved as a partner, we should also cancel the partner
     * 
     * @param r
     *            reservation we are canceling
     * @return canceled reservations
     */
    public List<Reservation> cancelReservation(Reservation r)
    {
        List<Reservation> canceled = new LinkedList<Reservation>();
        
        canceled.add(reservations.remove(r.getTraveler().GetName()));
        
        // if partner reservation, remove partner
        if (r.getRoomType() == RoomType.PARTNER)
        {
            canceled.add(reservations.remove(r.getPartnerName()));
        }
        
        // solo, find new partner for partner if possible
        if (r.getRoomType() == RoomType.SOLO && r.getPartner() != null)
        {
            // easiest way is to just re-add it
            addReservation(reservations.get(r.getPartnerName()));
        }
        
        return canceled;
    }
    
    /**
     * @return number of rooms required taking in to account that reservations with partners are in 1 room
     */
    public int getRoomsRequired()
    {
        Set<Reservation> counted = new HashSet<Reservation>();
        int count = 0;
        
        for (Reservation r : reservations.values())
        {
            // if reservation has been added because it was a partner, skip
            if (counted.contains(r))
                continue;
            
            counted.add(r);
            
            if (r.getPartner() != null)
            {
                Reservation partner = reservations.get(r.getPartnerName());
                if (partner != null)
                    counted.add(partner);
            }
            
            count++;
        }
        
        return count;
    }
    
    /**
     * to string for printing
     */
    public String toString()
    {
        String s = "";
        s += "Tour Offering:\nStart Date: " + dateFormat.format(startDate()) + "\n" + getTour() + "\nReservations:\n";
        
        for (Reservation r : reservations.values())
        {
            s += r.toString();
        }
        return s + "\n";
    }
    
    /**
     * main for unit testing
     * 
     * @param args
     */
    public static void main(String[] args)
    {
        Date d1 = new Date();
        Date d2 = new Date();
        TourOffering to1 = new TourOffering(d1);
        TourOffering to2 = new TourOffering(d2);
        
        System.out.println("Tour Offerings:\n" + to1 + "\n" + to2);
    }
}